using Engine.FileFormats3D.WR2TrafficFiles;
using SharpDX;
using System;
using System.Runtime.InteropServices;

namespace Engine.TrafficHelpers
{
    [StructLayout(LayoutKind.Auto)]
    public struct CrossroadTraffic
    {
        private bool isMainstreet;
        private CrossroadTrafficData laneRight, laneLeft, laneForward;
        private string name;
        private StreetNode streetNode;
        private const byte VERSION = 0x02;

        public bool IsMainstreet
        {
            get { return isMainstreet; }
            set { isMainstreet = value; }
        }

        public CrossroadTrafficData LaneForward
        {
            get { return laneForward; }
            set { laneForward = value; }
        }

        public CrossroadTrafficData LaneLeft
        {
            get { return laneLeft; }
            set { laneLeft = value; }
        }

        public CrossroadTrafficData LaneRight
        {
            get { return laneRight; }
            set { laneRight = value; }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public StreetNode StreetNode
        {
            get { return streetNode; }
            set { streetNode = value; }
        }

        public CrossroadTraffic(bool IsMainStreet, CrossroadTrafficData laneForward, CrossroadTrafficData laneLeft, CrossroadTrafficData laneRight)
        {
            this.streetNode = new StreetNode(0, 0, 0, 0, 0, 0, 0, false);
            this.isMainstreet = IsMainStreet;
            this.laneForward = laneForward;
            this.laneLeft = laneLeft;
            this.laneRight = laneRight;
            this.name = "noName";
        }

        public CrossroadTraffic(bool IsMainStreet, StreetNode streetnode, CrossroadTrafficData laneForward, CrossroadTrafficData laneLeft, CrossroadTrafficData laneRight)
            : this(IsMainStreet, laneForward, laneLeft, laneRight)
        {
            this.streetNode = streetnode;
        }

        public CrossroadTraffic(bool IsMainStreet, StreetNode streetnode, CrossroadTrafficData laneForward, CrossroadTrafficData laneLeft, CrossroadTrafficData laneRight, string name)
            : this(IsMainStreet, streetnode, laneForward, laneLeft, laneRight)
        {
            this.name = name;
        }

        public static CrossroadTraffic FromStream(System.IO.BinaryReader br)
        {
            switch (br.ReadByte())
            {
                case 0x01:
                    return LoadV1(br);

                case 0x02:
                    return LoadV2(br);
            }
            throw new Exception("Unsupport Version!");
        }

        private static CrossroadTraffic LoadV1(System.IO.BinaryReader br)
        {
            return new CrossroadTraffic(
                br.ReadBoolean(),
                StreetNode.FromStream(br, 0),
                CrossroadTrafficData.FromStream(br),
                CrossroadTrafficData.FromStream(br),
                CrossroadTrafficData.FromStream(br));
        }

        private static CrossroadTraffic LoadV2(System.IO.BinaryReader br)
        {
            return new CrossroadTraffic(
                br.ReadBoolean(),
                StreetNode.FromStream(br, 0),
                CrossroadTrafficData.FromStream(br),
                CrossroadTrafficData.FromStream(br),
                CrossroadTrafficData.FromStream(br),
                br.ReadString());
        }

        public void SetStreetnodeProperties(Vector3 position, double angle)
        {
            // Set angle.
            Vector2 rotation = GeoMath.GetVectorFromAngle(angle);
            StreetNode streetnode = this.StreetNode;
            //this.streetNode = new FileFormats3D.WR2TrafficFiles.StreetNode(0, position.X, position.Y,position.Z, rotation.X,0.0f, rotation.Y);
            streetnode.angle1 = rotation.X;
            streetnode.angle2 = rotation.Y;

            // set the position.
            streetnode.positionX = position.X;
            streetnode.positionY = position.Y;
            streetnode.positionZ = position.Z;

            this.StreetNode = streetnode;
        }

        public void ToStream(System.IO.BinaryWriter writer)
        {
            writer.Write(VERSION);
            writer.Write(this.isMainstreet);
            this.streetNode.Save(writer);
            this.laneForward.ToStream(writer);
            this.laneLeft.ToStream(writer);
            this.laneRight.ToStream(writer);
            writer.Write(this.name);
        }

        public override string ToString()
        {
            return this.name;
        }
    }

    [StructLayout(LayoutKind.Auto)]
    public struct CrossroadTrafficData
    {
        // Version number.
        private bool enable;

        private Lane lane;
        private float speed;
        private float tangentLengthStart, tangentLengthEnd;
        private const byte VERSION = 0x01;

        public override string ToString()
        {
            return string.Format("{0} Lane: {1} Speed: {2}", this.enable, this.lane, this.speed);
        }

        public bool Enable
        {
            get { return enable; }
            set { enable = value; }
        }

        public Lane Lane
        {
            get { return lane; }
            set { lane = value; }
        }

        public float Speed
        {
            get { return speed; }
            set { speed = value; }
        }

        public float TangentLengthEnd
        {
            get { return tangentLengthEnd; }
            set { tangentLengthEnd = value; }
        }

        public float TangentLengthStart
        {
            get { return tangentLengthStart; }
            set { tangentLengthStart = value; }
        }

        public CrossroadTrafficData(Lane lane, float speed, float tangentLengthStart, float tangentLengthEnd, bool enable)
        {
            this.lane = lane;
            this.speed = speed;
            this.tangentLengthStart = tangentLengthStart;
            this.tangentLengthEnd = tangentLengthEnd;
            this.enable = enable;
        }

        internal static CrossroadTrafficData FromStream(System.IO.BinaryReader br)
        {
            switch (br.ReadByte())
            {
                case 0x01:
                    return LoadV01(br);
            }
            throw new Exception("Unsupported Version!");
        }

        private static CrossroadTrafficData LoadV01(System.IO.BinaryReader br)
        {
            return new CrossroadTrafficData(Lane.FromStream(br),// Lane
                br.ReadSingle(), // speed
                br.ReadSingle(), // tangent length start
                br.ReadSingle(), // tangent length end
                br.ReadBoolean()); // enable
        }

        public void SetTangentStartLenght(float tangentStartLength)
        {
            this.tangentLengthStart = tangentStartLength;
        }

        internal void ToStream(System.IO.BinaryWriter writer)
        {
            writer.Write(VERSION);
            this.lane.ToStream(writer);
            writer.Write(this.speed);
            writer.Write(this.tangentLengthStart);
            writer.Write(this.tangentLengthEnd);
            writer.Write(this.enable);
        }
    }
}